﻿///<copyright>
/// Copyright © 2009 Stephen Styrchak. All Rights Reserved.
///</copyright>
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using Ferpect.ComponentModel.Sprites;
using Microsoft.Xna.Framework;
using System.Collections.Generic;

namespace Ferpect.ComponentModel.Design
{
    public class SpriteDesigner : ComponentDesigner
    {
        private Sprite sprite;
        private Matrix? hitTransform;
        private Vector2 positionBeforeAnchorChanged;

        public SpriteDesigner()
        {
            this.Visible = true;
        }

        public override void Initialize(IComponent component)
        {
            base.Initialize(component);
            this.sprite = (Sprite)component;
            sprite.Name = sprite.Site.Name;
            TextSprite textSprite = sprite as TextSprite;
            if (textSprite != null)
            {
                textSprite.Text = sprite.Name;
            }
        }

        public virtual bool SpriteCanMove
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// This is being invoked by the MenuScreenDesigner when this designer's component
        /// changes. Ideally, we'd want to move that handler into the SpriteDesigner class,
        /// but we only want one per designerHost. Something to improve later...
        /// </summary>
        public virtual void OnComponentChanged(ComponentChangedEventArgs args)
        {
            this.hitTransform = null;
            if (args.Member != null && args.Member.Name == "Anchor")
            {
                this.sprite.Offset = this.positionBeforeAnchorChanged - (this.sprite.Position - this.sprite.Offset);
            }
        }

        public void OnComponentChanging(ComponentChangingEventArgs args)
        {
            if (args.Member != null && args.Member.Name == "Anchor")
            {
                this.positionBeforeAnchorChanged = this.sprite.Position;
            }
        }

        public override void InitializeNewComponent(IDictionary defaultValues)
        {
            this.InitializeSpritePosition(defaultValues);

            base.InitializeNewComponent(defaultValues);
        }

        public override void InitializeExistingComponent(IDictionary defaultValues)
        {
            this.InitializeSpritePosition(defaultValues);

            base.InitializeExistingComponent(defaultValues);
        }

        public bool GetHitTest(System.Drawing.Point clientPt)
        {
            // Reverse the scaling and rotation applied during draw,
            // so that we can compare the point against the bounds of
            // the unscaled rectangle.
            Matrix transform = GetHitTransform();

            Vector2 point = new Vector2(clientPt.X, clientPt.Y);
            point = Vector2.Transform(point, transform);

            Vector2 size = this.sprite.GetSize();
            Rectangle boundingRect = new Rectangle(0, 0, (int)size.X, (int)size.Y);
            return boundingRect.Contains((int)point.X, (int)point.Y);
        }

        private Matrix GetHitTransform()
        {
            if (this.hitTransform == null)
            {
                Matrix translate = Matrix.CreateTranslation(-this.sprite.Position.X, -this.sprite.Position.Y, 0);
                Matrix scale = Matrix.CreateScale(1f / this.sprite.Scale, 1f / this.sprite.Scale, 1);
                Matrix rotate = Matrix.CreateRotationZ(-this.sprite.Rotation);
                Matrix retranslate = Matrix.CreateTranslation(this.sprite.Origin.X, this.sprite.Origin.Y, 0);
                this.hitTransform = translate * scale * rotate * retranslate;
            }
            return this.hitTransform.Value;
        }

        public virtual Sprite SpriteFromClientPoint(System.Drawing.Point clientPt)
        {
            return this.sprite;
        }

        private void InitializeSpritePosition(IDictionary defaultValues)
        {
            if (defaultValues.Contains("Location") && defaultValues.Contains("Parent"))
            {
                System.Windows.Forms.Control parent = defaultValues["Parent"] as System.Windows.Forms.Control;
                if (parent != null)
                {
                    try
                    {
                        System.Drawing.Point pt = (System.Drawing.Point)defaultValues["Location"];
                        pt = parent.PointToClient(pt);
                        ((Sprite)this.Component).Position = new Vector2(pt.X, pt.Y);
                    }
                    catch (InvalidCastException) { }
                }
            }
        }

        protected override void PostFilterAttributes(IDictionary attributes)
        {
            base.PostFilterAttributes(attributes);
            DesignTimeVisibleAttribute designTimeVisible = DesignTimeVisibleAttribute.No;
            attributes.Add(typeof(DesignTimeVisibleAttribute), designTimeVisible);
        }

        protected override void PostFilterProperties(IDictionary properties)
        {
            if (this.sprite != null)
            {
                properties["Visible"] = new OwnerPropertyDescriptor(TypeDescriptor.GetProperties(this)["Visible"], this, this.sprite);
            }
            base.PostFilterProperties(properties);
        }

        public override DesignerActionListCollection ActionLists
        {
            get
            {
                return base.ActionLists;
            }
        }

        public override DesignerVerbCollection Verbs
        {
            get
            {
                DesignerVerbCollection verbs = base.Verbs;

                verbs.Add(new DesignerVerb("Snap to Anchor", OnSnapToAnchor));
                verbs.Add(new DesignerVerb("Bring to Front", OnBringToFront));
                verbs.Add(new DesignerVerb("Send to Back", OnSendToBack));

                return verbs;
            }
        }

        private void OnBringToFront(object sender, EventArgs args)
        {
            Sprite item = (Sprite)this.Component;
            IDesignerHost host = (IDesignerHost)this.GetService(typeof(IDesignerHost));
            if (host != null)
            {
                ScreenControl screenControl = host.RootComponent as ScreenControl;
                Screen screen = null;
                if (screenControl != null)
                {
                    screen = screenControl.Component;
                }
                if (screen != null && screen.Sprites.Contains(item))
                {
                    screen.Sprites.Remove(item);
                    screen.Sprites.Add(item);
                }
            }
        }

        private void OnSendToBack(object sender, EventArgs args)
        {
            Sprite item = (Sprite)this.Component;
            IDesignerHost host = (IDesignerHost)this.GetService(typeof(IDesignerHost));
            if (host != null)
            {
                ScreenControl screenControl = host.RootComponent as ScreenControl;
                Screen screen = null;
                if (screenControl != null)
                {
                    screen = screenControl.Component;
                }
                if (screen != null && screen.Sprites.Contains(item))
                {
                    screen.Sprites.Remove(item);
                    screen.Sprites.Insert(0, item);
                }
            }
        }

        private void OnSnapToAnchor(object sender, EventArgs args)
        {
            Sprite item = (Sprite)this.Component;
            RelativeLocation anchor = item.Anchor;
            Vector2 size = item.GetSize();
            IViewportService viewport = (IViewportService)this.GetService(typeof(IViewportService));
            if (viewport != null)
            {
                item.SetAnchorPosition(viewport);
                PropertyDescriptor offsetProperty = TypeDescriptor.GetProperties(item)["Offset"];
                PropertyDescriptor relativeOriginProperty = TypeDescriptor.GetProperties(item)["RelativeOrigin"];
                if (offsetProperty != null)
                {
                    offsetProperty.SetValue(item, Vector2.Zero);
                }
                if (relativeOriginProperty != null)
                {
                    relativeOriginProperty.SetValue(item, anchor);
                }
            }
        }

        public override ICollection AssociatedComponents
        {
            get
            {
                ICollection collection = this.Component as ICollection;
                if (collection != null)
                {
                    return new ArrayList(collection);
                }
                return base.AssociatedComponents;
            }
        }

        [DefaultValue(true), Category("Behavior")]
        public bool Visible
        {
            get;
            set;
        }
    }
}
